﻿<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>加权抽签</title>
    <script src="https://unpkg.com/vue@next"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        .body {
            width: 100vw;
            height: 99vh;
            display: flex;
            overflow: hidden;
            align-items: center;
            justify-content: center;
            position: relative;
        }
        
        h1 {
            text-align: center;
            width: 300px;
            height: 50px;
            font-size: 40px;
            line-height: 50px;
        }
        #btn {
            position: absolute;
            top: 50px;
            left: calc(50vw - 100px);
        }
        button {
            padding: 15px;
            box-sizing: border-box;
            font-size: 30px;
            width: 200px;
        }
        
        .move {
            animation: big 1s ease-out forwards infinite;
        }
        
        .show {
            animation: small 1s ease-out forwards;
        }
        
        .alpha {
            display: block;
            animation: alpha 2s ease-out forwards;
        }

        .none {
            display: none;
        }
        
        @keyframes small {
            from {
                transform: scale(0.01);
                opacity: 0;
            }
            to {
                transform: scale(2);
                opacity: 1;
            }
        }
        
        @keyframes alpha {
            from {
                opacity: 0;
            }
            to {
                opacity: 1;
            }
        }
        
        @keyframes big {
            from {
                width: 50px;
                height: 50px;
                font-size: 40px;
                line-height: 50px;
                opacity: 1;
            }
            to {
                width: 650px;
                height: 650px;
                font-size: 640px;
                line-height: 650px;
                opacity: 0;
            }
        }
    </style>
    <style>
        #app {
            width: 100%;            
        }
        .test {
            width: 100%;
            height: 100vh;
            display: flex;
            overflow: auto;
            align-items: center;
            justify-content: flex-start;
            flex-flow: column nowrap;
            position: relative;
        }
    </style>
</head>

<body>
    <div id="app">
        <div class="body">     
            <button id="btn" @click="drawName" :class="btnClass">{{ btnText }}</button>
            <h1 id="h1" :class="textClassName">{{ showInfo }}</h1>
        </div>
        <div class="test">
            <button @click="test" :disabled="disabled"> {{ testBtnInfo }} </button>
            <div class="h1">
                <h1>{{name}}</h1>
            </div>
            <div>
                <ul>
                    <transition-group name="fade">
                        <Item v-for="(item,index) in userSort" :data="item" :index="index" :key="item.name"></Item>
                    </transition-group>
                </ul>
            </div>
        </div>
    </div>
    
</body>
<script type="text/javascript">
let Item = {
    props: ["data", "index"],
    template: `
            <li>
                {{index+1}} 姓名: {{data.name}} 权重: {{data.weight}} 次数: {{data.num}}
            </li>`,
}
const app = Vue.createApp({
    data() {
        return {
            name: "🙈",
            timer: null,
            testTimer: null,
            users: [
                {
                    name: "路人甲",
                    weight: 0,
                    num: 0
                },
                {
                    name: "路人乙",
                    weight: 0,
                    num: 0
                },
                {
                    name: "路人丙",
                    weight: 0,
                    num: 0
                },
                {
                    name: "路人丁",
                    weight: 0,
                    num: 0
                },
                {
                    name: "路人戊",
                    weight: 0,
                    num: 0
                },
                {
                    name: "路人己",
                    weight: 0,
                    num: 0
                },
                {
                    name: "路人庚",
                    weight: 0,
                    num: 0
                },
                {
                    name: "路人辛",
                    weight: 0,
                    num: 0
                },
                {
                    name: "路人壬",
                    weight: 0,
                    num: 0
                },
                {
                    name: "路人癸",
                    weight: 0,
                    num: 0
                }
            ],
            nameIndex: '',
            timeCount: 0,
            showInfo: '猜猜会是谁？',
            textClassName: '',
            btnClass: '',
            btnText: '开始抽',
            disabled: false,
            testBtnInfo: '测试概率'
        }
    },
    components: {
        Item
    },
    created() {
        // 获取本地存储的权重
        if (localStorage.getItem('user-weight')) {
            // 这里赋予要重写，考虑到num不要被覆盖掉
            this.users = JSON.parse(localStorage.getItem('user-weight')) 
        }
    },
    computed: {
        userSort() {
            return this.users.sort((a, b) => b.num - a.num)
        }
    },
    methods: {
        draw() {
            // 手写随机算法
            // let seed = new Date().getTime()
            // seed = (seed * 9301 + 49297) % 233280;
            // let rnd = seed / 233280.0;
            // this.nameIndex = Math.floor(rnd * this.users.length)
            // js 随机算法
            // return Math.floor(Math.random() * this.users.length)
            // 第一步是反向计算权重，即权重越大概率越小
            // 得到总权树和 通过map生成权数数组用于比对 最好有两个数区间这样好比较
            // 第二步随机总全权数
            // 生成随机数与权重数组进行比对
            let weightArr = this.users.map(item => {
                return item.weight
            })
            const Max = Math.max(...weightArr) + 1
            let weightIntervalArr = []
            let totalDeWeight = 0
            weightArr.reduce((acc, item, index, arr) => {
                let deWeight = Max - item
                let max = acc + deWeight
                weightIntervalArr.push([acc, max])
                if (index === arr.length - 1) {
                    totalDeWeight = max
                }
                return max
            }, 0)
            let deWeightNum = Math.floor(Math.random() * totalDeWeight)
            let Index = -1
            weightIntervalArr.forEach((item, index) => {
                if( item[0] <= deWeightNum && item[1] > deWeightNum ) {
                    Index = index
                }
            })
            return Index
        },
        test() {
            // 不需要长时间计时，但要控制频率
            this.disabled = true
            this.testBtnInfo = '稍等~~'
            for (i=0; i < 10000; i++) {
                let randIndex = this.draw()
                if (this.users[randIndex].num) {
                    this.users[randIndex].num++
                } else {
                    this.users[randIndex].num = 1;
                }
            }
            this.testBtnInfo = '测试概率'
            this.disabled = false
        },
        async move() {
            this.timeCount--
            this.showInfo = this.timeCount
            if (this.timeCount === -1) {
                this.textClassName = 'show'
                clearInterval(this.timer)
                this.nameIndex = this.draw() // 权重算法
                this.showInfo = this.users[this.nameIndex].name
                this.btnClass = 'alpha'
                this.btnText = '换个姿势再抽一次'
            }
        },
        drawName() {
            this.timeCount = 3
            this.showInfo = this.timeCount
            this.textClassName = 'move'
            this.timer = setInterval(this.move, 500)
            this.btnClass = 'none'
        }
    }
})
app.mount('#app')
</script>

</html>